package org.lyq.erp.config.interceptor;

import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.lyq.erp.auth.employee.pojo.Employee;
import org.lyq.erp.auth.employee.service.EmployeeService;
import org.lyq.erp.auth.menu.pojo.Menu;
import org.lyq.erp.config.constant.ConfigConstant;
import org.lyq.erp.config.exception.myexception.MyException;
import org.lyq.erp.config.redis.RedisUtils;
import org.lyq.erp.utils.string.ObjectEmptyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 系统登录拦截器类
 * 
 * @ClassName: LoginInterceptor
 * @Description:
 * @author LQY(廖先生)
 * @date 2017年12月29日 下午12:54:25
 *
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

	@Autowired
	private EmployeeService employeeService;

	@Autowired
	private RedisUtils redisUtils;

	/**
	 * preHandle方法是进行处理器拦截用的，顾名思义，该方法将在Controller处理之前进行调用，SpringMVC中的Interceptor拦截器是链式的，可以同时存在
	 * 多个Interceptor，然后SpringMVC会根据声明的前后顺序一个接一个的执行，而且所有的Interceptor中的preHandle方法都会在
	 * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的，这种中断方式是令preHandle的返
	 * 回值为false，当preHandle的返回值为false的时候整个请求就结束了。
	 */
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return loginValidation(request, response);
	}

	/**
	 * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的，它的执行时间是在处理器进行处理之
	 * 后，也就是在Controller的方法调用之后执行，但是它会在DispatcherServlet进行视图的渲染之前执行，也就是说在这个方法中你可以对ModelAndView进行操
	 * 作。这个方法的链式结构跟正常访问的方向是相反的，也就是说先声明的Interceptor拦截器该方法反而会后调用，这跟Struts2里面的拦截器的执行过程有点像，
	 * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法，Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor
	 * 或者是调用action，然后要在Interceptor之前调用的内容都写在调用invoke之前，要在Interceptor之后调用的内容都写在调用invoke方法之后。
	 */
	@SuppressWarnings("unchecked")
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {

		// 菜单的返回
		List<Menu> mList = (List<Menu>) request.getSession().getAttribute(ConfigConstant.ALL_MENUS_LIST);
		try {
			modelAndView.addObject("mList", mList);
		} catch (Exception e) {

		}

		/*
		 * 拦截器拦截Ajax请求时，（modelAndView为null） 简单的说,就是发送的是ajax异步请求，只是想获取一个json封装的数据，
		 * 而在拦截器中却硬生生的加了个modelAndView， 而且controller层返回的数据中也并没有实例modelAndView。
		 * 这个modelAndView也不是ajax请求想要的返回数据。
		 */
		// 获取请求中菜单menuUuid的值,返回出去让前端页面显示访问的菜单
		String menuUuid = request.getParameter("menuUuid");
		if (menuUuid != null) {
			if (modelAndView != null) {
				modelAndView.addObject("menuUuid", menuUuid);
			}
		}

		// 被迫下线
		forcedDownline(request);

	}

	/**
	 * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后，也就是DispatcherServlet渲染了视图执行，
	 * 这个方法的主要作用是用于清理资源的，当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。
	 */
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e)
			throws Exception {
	}

	private void forcedDownline(HttpServletRequest request) {
		// 首先通过请求区分员工的改变和其他的改变
		String pathName = request.getServletPath();
		if (ObjectEmptyUtils.isNotEmpty(pathName)) {
			// 如果员工改变
			if (pathName.contains("employee")) {
				String empUuid = request.getParameter("empUuid");
				if (empUuid != null) {
					redisUtils.set("userSeesion:"+request.getParameter("empUuid").trim(), ConfigConstant.STATUS_ID_0);
				}
			}

			// 修改后
			String[] loginoutUrls = (String[]) request.getServletContext()
					.getAttribute(ConfigConstant.IS_INTERCEPTOR_LOGINOUT_URL);
			if (loginoutUrls != null) {
				for (String url : loginoutUrls) {
					if (pathName.trim().equals(url.trim())) {
						List<Employee> list = employeeService.selectAllEmployee(new Employee());
						for (Employee emp : list) {
							redisUtils.set("userSeesion:"+emp.getEmpUuid() + "", ConfigConstant.STATUS_ID_0);
						}
					}
				}
			}

			// 如果角色，资源，菜单改变，将员工的id为key值，存储状态为0，视为要重新登录，被迫下线，回去超级管理员用户id数组
			String[] ids = (String[]) request.getServletContext().getAttribute(ConfigConstant.ADMIN_ID);
			// 恢复超级用户的登录状态
			for (String id : ids) {
				redisUtils.set("userSeesion:"+id.trim(), ConfigConstant.STATUS_ID_1);
			}
		}
	}

	/**
	 * 
	 * @Title: loginValidation @Description: TODO(登陆前拦截器的校验) @param @param
	 * request @param @param response @param @return @param @throws IOException
	 * 参数 @return boolean 返回类型 @throws
	 */
	private boolean loginValidation(HttpServletRequest request, HttpServletResponse response) throws IOException {
		// 请求路径的信息
		String pathName = request.getServletPath().trim();
		if (ObjectEmptyUtils.isEmptyString(pathName)) {
			response.sendRedirect(request.getContextPath() + ConfigConstant.LOGIN_URL);
			return false;
		}

		// 放弃未登录拦截的请求
		String[] donotIntorUrls = (String[]) request.getServletContext()
				.getAttribute(ConfigConstant.DONOT_INTERCEPTOR_URL);
		if (donotIntorUrls != null) {
			for (String url : donotIntorUrls) {
				if (pathName.equals(url.trim())) {
					return true;
				}
			}
		}

		// 获取session中的用户是否存在
		Employee emp = (Employee) request.getSession().getAttribute(ConfigConstant.USER_LOGIN);
		// 判定用户是否登录,如果emp为空，表示没有登录，就跳转到登录界面
		if (emp == null) {
			response.sendRedirect(request.getContextPath() + ConfigConstant.LOGIN_URL);
			return false;
		}

		// 如果不为空，再用用户id从redis中获取一个参数值，如果该参数值为1(表示系统中的权限未改变),0(表示系统权限改变需要重新登录)
		try {
			String id = (String) redisUtils.get("userSeesion:"+emp.getEmpUuid());
			if (ConfigConstant.STATUS_ID_0.equals(id)) {
				request.getSession().invalidate();
				response.sendRedirect(request.getContextPath() + ConfigConstant.LOGIN_URL + "?str=2");
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 判定该用户是否需要权限访问该资源
		String allRes = (String) request.getServletContext().getAttribute(ConfigConstant.ALL_RES);
		if (ObjectEmptyUtils.isNotEmptyString(allRes) && allRes.indexOf(pathName) < 0)
			return true;

		// 获取自己的所属资源
		String myAllRes = (String) request.getSession().getAttribute(ConfigConstant.MY_ALL_RES);
		if (myAllRes.contains(pathName))
			return true;
		else
			throw new MyException("您的权限不足，不予许返问");

	}
}